﻿using OSGeo.GDAL;
using OSGeo.OGR;
using OSGeo.OSR;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;

namespace ConsoleApp
{
    public class GeoTiffSplitter
    {
        public const string GEO_TIFF_SPLIT_DIR_NAME = "tiffsplits";
        public const string IR_GEO_TIFF_SPLIT_MAP_JSON_FILE_NAME = GEO_TIFF_SPLIT_DIR_NAME + "_T.json";
        public const string VS_GEO_TIFF_SPLIT_MAP_JSON_FILE_NAME = GEO_TIFF_SPLIT_DIR_NAME + "_V.json";

        private static GeoPoint PixelToGeo(int x, int y, double[] transform,
                                    CoordinateTransformation ct)
        {
            double geoX = transform[0] + x * transform[1] + y * transform[2];
            double geoY = transform[3] + x * transform[4] + y * transform[5];
            double[] argout = new double[6];
            ct.TransformPoint(argout, geoX, geoY, 0);
            return new GeoPoint { Lon = argout[1], Lat = argout[0] };
        }



        public static void SplitGeoTiffWithColorTable(string inputPath, string outputDir,
                                      int tileSize = 512, bool overwrite = false, bool isTheramlImg = true)
        {
            if (!File.Exists(inputPath))
                throw new FileNotFoundException("输入文件不存在", inputPath);

            Directory.CreateDirectory(outputDir);
            // 指定GDAL原生库的路径（替换为你的实际路径）            
            GdalConfiguration.ConfigureGdal();
            GdalConfiguration.ConfigureOgr();

            Gdal.AllRegister();
            Ogr.RegisterAll();

            using (Dataset dataset = Gdal.Open(inputPath, Access.GA_ReadOnly))
            {
                if (dataset == null)
                    throw new InvalidOperationException("无法读取GeoTIFF文件");

                double[] geoTransform = new double[6];
                dataset.GetGeoTransform(geoTransform);

                SpatialReference srcSrs = new SpatialReference(dataset.GetProjection());
                SpatialReference destSrs = new SpatialReference("");
                destSrs.SetWellKnownGeogCS("WGS84");
                CoordinateTransformation ct = new CoordinateTransformation(srcSrs, destSrs);

                // 获取全局颜色表（首波段）
                ColorTable colorTable = null;
                using (Band firstBand = dataset.GetRasterBand(1))
                {
                    colorTable = firstBand.GetRasterColorTable()?.Clone();
                }

                List<GeoTileInfo> tiles = new List<GeoTileInfo>();

                // 改为分块遍历（而非波段遍历）
                for (int y = 0; y < dataset.RasterYSize; y += tileSize)
                {
                    for (int x = 0; x < dataset.RasterXSize; x += tileSize)
                    {
                        ProcessTile(dataset, x, y, tileSize, outputDir,
                                  geoTransform, ct, colorTable,
                                  overwrite, isTheramlImg, tiles);
                    }
                }

                File.WriteAllText(
                    Path.Combine(outputDir, isTheramlImg ? IR_GEO_TIFF_SPLIT_MAP_JSON_FILE_NAME : VS_GEO_TIFF_SPLIT_MAP_JSON_FILE_NAME),
                    Newtonsoft.Json.JsonConvert.SerializeObject(tiles,
                    Newtonsoft.Json.Formatting.Indented)
                );
            }
        }

        private static void ProcessTile(Dataset dataset, int xStart, int yStart,
                                      int tileSize, string outputDir,
                                      double[] geoTransform, CoordinateTransformation ct,
                                      ColorTable colorTable, bool overwrite, bool isTheramlImg,
                                      List<GeoTileInfo> tiles)
        {
            int width = Math.Min(tileSize, dataset.RasterXSize - xStart);
            int height = Math.Min(tileSize, dataset.RasterYSize - yStart);
            int bandCount = dataset.RasterCount;

            // 读取所有波段数据
            byte[][] tileData = new byte[bandCount][];
            for (int b = 0; b < bandCount; b++)
            {
                using (Band band = dataset.GetRasterBand(b + 1))
                {
                    tileData[b] = ReadBandData(band, xStart, yStart, width, height);
                }
            }
            string imageType = isTheramlImg ? "T" : "V";
            string fileName = $"tile_{yStart / tileSize}_{xStart / tileSize}_{imageType}.png";
            string filePath = Path.Combine(outputDir, fileName);

            if (!overwrite && File.Exists(filePath))
            {
                Console.WriteLine($"跳过 {fileName}");
                return;
            }
            // 新增：检查是否为空白切片
            if (IsTileBlank(tileData))
            {
                Console.WriteLine($"跳过空白切片 {fileName}");
                return;
            }
            // 颜色处理决策
            if (bandCount == 1 && colorTable != null)
            {
                SaveWithColorTable(tileData[0], colorTable, width, height, filePath);
            }
            else if (bandCount >= 3)
            {
                SaveAsTrueColor(tileData[0], tileData[1], tileData[2], width, height, filePath);
            }
            else
            {
                GeneratePseudocolor(tileData[0], width, height, filePath);
            }

            // 记录元数据（每个分块只记录一次）
            tiles.Add(new GeoTileInfo
            {
                FileName = fileName,
                X = xStart,
                Y = yStart,
                TopLeft = PixelToGeo(xStart, yStart, geoTransform, ct),
                BottomRight = PixelToGeo(xStart + width, yStart + height, geoTransform, ct),
                Center = PixelToGeo(xStart + width / 2, yStart + height / 2, geoTransform, ct)
            });
        }

        private static byte[] ReadBandData(Band band, int xStart, int yStart,
                                         int width, int height)
        {
            byte[] buffer = new byte[width * height];
            band.ReadRaster(xStart, yStart, width, height,
                          buffer, width, height, 0, 0);
            return buffer;
        }

        private static void SaveWithColorTable(byte[] data, ColorTable ct,
                                             int width, int height, string path)
        {
            using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed))
            {
                // 设置调色板
                ColorPalette palette = bmp.Palette;
                for (int i = 0; i < ct.GetCount(); i++)
                {
                    ColorEntry ce = ct.GetColorEntry(i);
                    palette.Entries[i] = Color.FromArgb(ce.c4, ce.c1, ce.c2, ce.c3);
                }
                bmp.Palette = palette;

                // 快速写入数据
                BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height),
                                              ImageLockMode.WriteOnly,
                                              PixelFormat.Format8bppIndexed);
                Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
                bmp.UnlockBits(bmpData);
                bmp.Save(path, ImageFormat.Png);
            }
        }

        private static void SaveAsTrueColor(byte[] r, byte[] g, byte[] b,
                                          int width, int height, string path)
        {
            using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
            {
                BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height),
                                                ImageLockMode.WriteOnly,
                                                PixelFormat.Format24bppRgb);

                unsafe
                {
                    byte* ptr = (byte*)bmpData.Scan0;
                    for (int i = 0; i < width * height; i++)
                    {
                        ptr[2] = r[i]; // R
                        ptr[1] = g[i]; // G
                        ptr[0] = b[i]; // B
                        ptr += 3;
                    }
                }
                bmp.UnlockBits(bmpData);
                bmp.Save(path, ImageFormat.Png);
            }
        }

        public static void GeneratePseudocolor(byte[] data, int width,
                                              int height, string path)
        {
            // 自动计算极值
            byte min = byte.MaxValue, max = byte.MinValue;
            foreach (byte val in data)
            {
                if (val < min) min = val;
                if (val > max) max = val;
            }

            using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
            {
                BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height),
                                                ImageLockMode.WriteOnly,
                                                PixelFormat.Format24bppRgb);

                unsafe
                {
                    byte* ptr = (byte*)bmpData.Scan0;
                    for (int i = 0; i < data.Length; i++)
                    {
                        Color color = HeatMapColor(data[i], min, max);
                        ptr[2] = color.R;
                        ptr[1] = color.G;
                        ptr[0] = color.B;
                        ptr += 3;
                    }
                }
                bmp.UnlockBits(bmpData);
                bmp.Save(path, ImageFormat.Png);
            }
        }

        // 热力图颜色映射（可自定义）
        private static Color HeatMapColor(byte value, byte min, byte max)
        {
            float ratio = (float)(value - min) / (max - min);
            return Color.FromArgb(
                (int)(255 * ratio),       // R
                (int)(255 * (1 - ratio)), // G
                0                         // B
            );
        }

        // 新增：多波段空白检查方法
        public static bool IsTileBlank(byte[][] tileData)
        {
            if (tileData == null || tileData.Length == 0) return true;

            int bandCount = tileData.Length;
            int pixelCount = tileData[0].Length;

            // 检查所有波段的所有像素是否一致
            byte baseValue = tileData[0][0];
            for (int b = 0; b < bandCount; b++)
            {
                byte[] data = tileData[b];
                if (data.Length != pixelCount) return false;

                foreach (byte val in data)
                {
                    if (val != baseValue) return false;
                }
            }
            return true;
        }

        public static List<GeoTileInfo> readGeoTileInfosFromFile(string geotiffSlitDir, bool isTheramlImg = true)
        {
            string tiffsplitsJsonFile = Path.Combine(geotiffSlitDir, isTheramlImg ? IR_GEO_TIFF_SPLIT_MAP_JSON_FILE_NAME : VS_GEO_TIFF_SPLIT_MAP_JSON_FILE_NAME);
            if (File.Exists(tiffsplitsJsonFile))
            {
                string content = DealFileHelper.readStringFromFile(tiffsplitsJsonFile);
                if (!string.IsNullOrEmpty(content))
                {
                    return Newtonsoft.Json.JsonConvert.DeserializeObject<List<GeoTileInfo>>(content);
                }
            }
            return null;
        }


        /// <summary>
        /// 计算原始图像任意像素点的地理坐标（WGS84）
        /// </summary>
        /// <param name="pixelX">原始图像X像素坐标</param>
        /// <param name="pixelY">原始图像Y像素坐标</param>
        /// <param name="geoTransform">6参数地理变换矩阵</param>
        /// <param name="sourceProjection">原始坐标系WKT</param>
        /// <returns>地理坐标点</returns>
        public static GeoPoint GetGeoCoordinate(int pixelX, int pixelY,
            double[] geoTransform, string sourceProjection)
        {
            ValidateGeoTransform(geoTransform);

            // 计算原始坐标
            double geoX = geoTransform[0] + pixelX * geoTransform[1] + pixelY * geoTransform[2];
            double geoY = geoTransform[3] + pixelX * geoTransform[4] + pixelY * geoTransform[5];

            // 创建坐标转换
            using (SpatialReference srcSrs = new SpatialReference(sourceProjection))
            using (SpatialReference destSrs = new SpatialReference(""))
            {
                destSrs.SetWellKnownGeogCS("WGS84");
                using (CoordinateTransformation ct = new CoordinateTransformation(srcSrs, destSrs))
                {
                    double[] output = new double[3];
                    ct.TransformPoint(output, geoX, geoY, 0);
                    return new GeoPoint
                    {
                        Lon = output[1],
                        Lat = output[0]
                    };
                }
            }
        }
        private static void ValidateGeoTransform(double[] geoTransform)
        {
            if (geoTransform == null || geoTransform.Length != 6)
                throw new ArgumentException("无效的地理变换参数");

            // 检查是否有旋转分量（非南北方向的图像）
            if (geoTransform[2] != 0 || geoTransform[4] != 0)
                throw new NotSupportedException("当前不支持旋转坐标系");
        }

    }
}
